/* 
 PureMVC Java MultiCore Port by Ima OpenSource <opensource@ima.eu>
 Maintained by Anthony Quinault <anthony.quinault@puremvc.org>
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
 */
package org.puremvc.java.multicore.core.controller;

import java.util.HashMap;
import java.util.Map;

import org.puremvc.java.multicore.core.view.View;
import org.puremvc.java.multicore.interfaces.ICommand;
import org.puremvc.java.multicore.interfaces.IController;
import org.puremvc.java.multicore.interfaces.IFunction;
import org.puremvc.java.multicore.interfaces.INotification;
import org.puremvc.java.multicore.patterns.observer.Observer;

/**
 * A Multiton <code>IController</code> implementation.
 * 
 * <P>
 * In PureMVC, the <code>Controller</code> class follows the 'Command and
 * Controller' strategy, and assumes these responsibilities:
 * <UL>
 * <LI>Remembering which <code>ICommand</code> are intended to handle which
 * <code>INotifications</code>.</LI>
 * <LI>Registering itself as an <code>IObserver</code> with the
 * <code>View</code> for each <code>INotification</code> that it has an
 * <code>ICommand</code> mapping for.</LI>
 * <LI>Creating a new instance of the proper <code>ICommand</code> to handle a
 * given <code>INotification</code> when notified by the <code>View</code>.</LI>
 * <LI>Calling the <code>ICommand</code>'s <code>execute</code> method, passing
 * in the <code>INotification</code>.</LI>
 * </UL>
 * 
 * <P>
 * Your application must register <code>ICommands</code> with the Controller.
 * <P>
 * The simplest way is to subclass </code>Facade</code>, and use its
 * <code>initializeController</code> method to add your registrations.
 * 
 * @see org.puremvc.java.multicore.core.View View
 * @see org.puremvc.java.multicore.patterns.observer.Observer Observer
 * @see org.puremvc.java.multicore.patterns.observer.Notification Notification
 * @see org.puremvc.java.multicore.patterns.command.SimpleCommand SimpleCommand
 * @see org.puremvc.java.multicore.patterns.command.MacroCommand MacroCommand
 */
public class Controller implements IController {

    /**
     * Mapping of Notification names to Command Class references
     */
    protected Map<String, ICommand> commandMap;

    /**
     * Local reference to View
     */
    protected View view;

    /**
     * The Multiton Key for this Core
     */
    protected String multitonKey;

    protected static Map<String, Controller> instanceMap = new HashMap<String, Controller>();

    /**
     * Constructor.
     * 
     * <P>
     * This <code>IController</code> implementation is a Multiton, so you should
     * not call the constructor directly, but instead call the static Factory
     * method, passing the unique key for this instance
     * <code>Controller.getInstance( multitonKey )</code>
     * 
     * @throws Error
     *             Error if instance for this Multiton key has already been
     *             constructed
     * 
     */
    protected Controller(String key) {
        multitonKey = key;
        instanceMap.put(multitonKey, this);
        this.commandMap = new HashMap<String, ICommand>();
        initializeController();
    }

    /**
     * Initialize the Multiton <code>Controller</code> instance.
     * 
     * <P>
     * Called automatically by the constructor.
     * </P>
     * 
     * <P>
     * Note that if you are using a subclass of <code>View</code> in your
     * application, you should <i>also</i> subclass <code>Controller</code> and
     * override the <code>initializeController</code> method in the following
     * way:
     * </P>
     * 
     * <listing> // ensure that the Controller is talking to my IView
     * implementation override public function initializeController( ) : void {
     * view = MyView.getInstance(); } </listing>
     * 
     * @return void
     */
    protected void initializeController() {
        this.view = View.getInstance(multitonKey);
    }

    /**
     * <code>Controller</code> Multiton Factory method.
     * 
     * @return the Multiton instance of <code>Controller</code>
     */
    public synchronized static Controller getInstance(String key) {
        if (instanceMap.get(key) == null) {
            new Controller(key);
        }
        return instanceMap.get(key);
    }

    /**
     * If an <code>ICommand</code> has previously been registered to handle a
     * the given <code>INotification</code>, then it is executed.
     * 
     * @param note
     *            an <code>INotification</code>
     */
    public void executeCommand(INotification note) {
        // No reflexion in GWT
        // ICommand commandInstance = (ICommand) commandClassRef.newInstance();
        ICommand commandInstance = (ICommand) this.commandMap.get(note
                .getName());
        if (commandInstance != null) {
            commandInstance.initializeNotifier(multitonKey);
            commandInstance.execute(note);
        }
    }

    /**
     * Register a particular <code>ICommand</code> class as the handler for a
     * particular <code>INotification</code>.
     * 
     * <P>
     * If an <code>ICommand</code> has already been registered to handle
     * <code>INotification</code>s with this name, it is no longer used, the new
     * <code>ICommand</code> is used instead.
     * </P>
     * 
     * The Observer for the new ICommand is only created if this the first time
     * an ICommand has been regisered for this Notification name.
     * 
     * @param notificationName
     *            the name of the <code>INotification</code>
     * @param command
     *            an instance of <code>ICommand</code>
     */
    public void registerCommand(String notificationName, ICommand command) {
        if (null != this.commandMap.put(notificationName, command))
            return;
        this.view.registerObserver(notificationName, new Observer(
                new IFunction() {
                    public void onNotification(INotification notification) {
                        executeCommand(notification);
                    }
                }, this));
    }

    /**
     * Remove a previously registered <code>ICommand</code> to
     * <code>INotification</code> mapping.
     * 
     * @param notificationName
     *            the name of the <code>INotification</code> to remove the
     *            <code>ICommand</code> mapping for
     */
    public void removeCommand(String notificationName) {
        // if the Command is registered...
        if (hasCommand(notificationName)) {
            // remove the observer
            view.removeObserver(notificationName, this);
            this.commandMap.remove(notificationName);
        }
    }

    /**
     * Remove an IController instance
     * 
     * @param multitonKey
     *            of IController instance to remove
     */
    public synchronized static void removeController(String key) {
        instanceMap.remove(key);
    }

    /**
     * Check if a Command is registered for a given Notification
     * 
     * @param notificationName
     * @return whether a Command is currently registered for the given
     *         <code>notificationName</code>.
     */
    public boolean hasCommand(String notificationName) {
        return commandMap.containsKey(notificationName);
    }
}
